<!DOCTYPE html>
            <html lang="en" data-theme="light">

                <head>
                    <meta charset="utf-8">

                    <title>The Book of Shaders</title>
                    <link href="/favicon.gif" rel="shortcut icon" />
                    <meta name="keywords" content="shader,openGL,WebGL,GLSL,book,procedural,generative" />
                    <meta name="description" content="Gentle step-by-step guide through the abstract and complex universe of Fragment Shaders." />

                    <!— Open Graph data —>
                    <meta property="og:type" content="article" />
                    <meta property="og:title" content="The Book of Shaders" />
                    <meta property="og:site_name" content="The Book of Shaders" />
                    <meta property="og:description" content="Gentle step-by-step guide through the abstract and complex universe of Fragment Shaders." />
                    <meta property="og:image" content="http://thebookofshaders.com/thumb.png" />
                    <meta property="og:image:secure_url" content="https://thebookofshaders.com/thumb.png" />
                    <meta property="og:image:type" content="image/png" />
                    <meta property="og:image:width" content="500" />
                    <meta property="og:image:height" content="500" />

                    <link href="/favicon.gif" rel="shortcut icon" />

                    <!-- Highlight -->
                    <link type="text/css" rel="stylesheet" href="/css/github.css">
                    <script type="text/javascript" src="/src/highlight.min.js"></script>
                    <!-- GlslCanvas -->
                    <script type="text/javascript" src="/src/glslCanvas/GlslCanvas.js"></script>

                    <!-- GlslEditor -->
                    <link type="text/css" rel="stylesheet" href="/src/glslEditor/glslEditor.css">
                    <script type="application/javascript" src="/src/glslEditor/glslEditor.js"></script>

                    <!-- GlslGallery -->
                    <link type="text/css" rel="stylesheet" href="/src/glslGallery/glslGallery.css">
                    <script type="application/javascript" src="/src/glslGallery/glslGallery.js"></script>

                    <!-- Main style -->
                    <link type="text/css" rel="stylesheet" href="/css/styles.css">

                </head>

                <body>
                    <div class="toc-header">
                        <p class="subtitle"><a href="https://thebookofshaders.com/">The Book of Shaders</a> by <a href="http://patriciogonzalezvivo.com">Patricio Gonzalez Vivo</a> & <a href="http://jenlowe.net">Jen Lowe</a> </p>
                        <p>  <a href=".">中文版</a> - <a href="https://thebookofshaders.com/">English</a></p>
                    </div>
                    <hr>

                    <div id="content"><h2 id="2d-matrices-二维矩阵">2D Matrices 二维矩阵</h2>
<p><canvas id="custom" class="canvas" data-fragment-url="matrix.frag"  width="700px" height="200px"></canvas></p>
<h3 id="平移">平移</h3>
<p>之前的章节我们学习了如何制作一些图形 - 而如何移动它们的技巧则是借助移动它们自身的参考坐标系。我们只需要给 <code>st</code> 变量加上一个包含每个片段的位置的向量。这样就移动了整个坐标系。</p>
<p><img src="translate.jpg" alt=""></p>
<p>还是画着比较更容易解释，如上图所示：</p>
<ul>
<li>取消下面代码中第35行的注释，看下坐标空间是如何平移的。</li>
</ul>
<div class="codeAndCanvas" data="cross-translate.frag"></div>

<p>现在尝试下下面的练习：</p>
<ul>
<li>结合 <code>u_time</code> 和造型函数来移动十字，并试着让它有趣一点。找一个你觉得你感兴趣的某种运动形式，让这个十字也这样运动。记录“真实世界”的一些现象或许对你有所启发 — 可以是波的运动，摆动，弹球，汽车的加速运动，一辆自行车的刹车。</li>
</ul>
<h3 id="旋转">旋转</h3>
<p>要移动物体，我们同样需要移动整个空间（坐标）系统。为此我们将使用一个<a href="http://en.wikipedia.org/wiki/Matrix_%28mathematics%29">矩阵</a>。矩阵是一个通过行和列定义的一组数。用矩阵乘以一个向量是用一组精确的规则定义的，这样做是为了以一组特定的方式来改变向量的值。</p>
<p><a href="https://en.wikipedia.org/wiki/Matrix"><img src="matrixes.png" alt="Wikipedia entry for Matrix (mathematics) "></a></p>
<p>GLSL本身支持2维，3维和4维方阵（m*m矩阵）：<a href="../glossary/?search=mat2"><code>mat2</code></a> (2x2), <a href="../glossary/?search=mat3"><code>mat3</code></a> (3x3) 和 <a href="../glossary/?search=mat4"><code>mat4</code></a> (4x4)。GLSL同样支持矩阵相乘 (<code>*</code>)和特殊矩阵函数(<a href="../glossary/?search=matrixCompMult"><code>matrixCompMult()</code></a>)。</p>
<p>基于矩阵的特性，我们便有可能构造一个矩阵来产生特定的作用。比如我们可以用一个矩阵来平移一个向量：</p>
<p><img src="3dtransmat.png" alt=""></p>
<p>更有趣的是，我们可以用矩阵来旋转坐标系统：</p>
<p><img src="rotmat.png" alt=""></p>
<p>看下下面构成2维旋转的矩阵的代码。这个函数根据上面的<a href="http://en.wikipedia.org/wiki/Rotation_matrix">公式</a>，将二维向量绕 <code>vec2(0.0)</code> 点旋转。</p>
<pre><code class="language-glsl">mat2 rotate2d(float _angle){
    return mat2(cos(_angle),-sin(_angle),
                sin(_angle),cos(_angle));
}
</code></pre>
<p>根据以往我们画形状的方式，这并不是我们想要的。我们的十字是画在画布中心的，对应于点 <code>vec2(0.5)</code> 。所以，再旋转坐标空间之前，我们需要先把图形移到中心点，坐标 <code>vec2(0.0)</code> ，再旋转坐标空间，最后在移动回原点。</p>
<p><img src="rotate.jpg" alt=""></p>
<p>就像下面的代码：</p>
<div class="codeAndCanvas" data="cross-rotate.frag"></div>

<p>试试下面的练习：</p>
<ul>
<li><p>取消第45行的代码，看看会发生什么。</p>
</li>
<li><p>在37行和39行，将旋转之前的平移注释掉，观察结果。</p>
</li>
<li><p>用旋转改进在平移练习中模拟的动画。</p>
</li>
</ul>
<h3 id="缩放">缩放</h3>
<p>我们看到了如何用矩阵平移和旋转物体。（或者更准确的说，如何通过变换坐标系统来旋转和移动物体。）如果你用过3D建模软件或者 Processing中的 pushmatrix 和 popmatrix 函数，你会知道矩阵也可以被用来缩放物体的大小。</p>
<p><img src="scale.png" alt=""></p>
<p>根据上面的公式，我们知道如何构造一个2D缩放矩阵：</p>
<pre><code class="language-glsl">mat2 scale(vec2 _scale){
    return mat2(_scale.x,0.0,
                0.0,_scale.y);
}
</code></pre>
<div class="codeAndCanvas" data="cross-scale.frag"></div>


<p>试试下面的练习，尝试深入理解矩阵的工作机制：</p>
<ul>
<li><p>取消上面代码中的第42行来观察空间坐标是如何被缩放的。</p>
</li>
<li><p>看看注释掉37和39行，变换之前和之后的缩放，会发生什么。</p>
</li>
<li><p>试着结合旋转矩阵和缩放矩阵。注意他们的先后顺序。先乘以一个矩阵，再乘以向量。</p>
</li>
<li><p>现在你知道如何画不同的图形，知道如何移动，旋转和缩放它们，是时候用这些来创作了。设计一个<a href="https://www.pinterest.com/patriciogonzv/huds/">fake UI or HUD (heads up display)</a>。参考<a href="https://www.shadertoy.com/user/ndel">Ndel</a>在ShaderToy上的例子。</p>
</li>
</ul>
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/4s2SRt?gui=true&t=10&paused=true" allowfullscreen></iframe>

<h3 id="other-uses-for-matrices-yuv-color-矩阵的其他应用：yuv-颜色">Other uses for matrices: YUV color 矩阵的其他应用：YUV 颜色</h3>
<p><a href="http://en.wikipedia.org/wiki/YUV">YUV</a> 是个用来模拟照片和视频的编码的色彩空间。这个色彩空间考虑人类的感知，减少色度的带宽。</p>
<p>下面的代码展现一种利用GLSL中的矩阵操作来切换颜色模式的有趣可能。</p>
<div class="codeAndCanvas" data="yuv.frag"></div>


<p>正如你所见，我们用对向量乘以矩阵的方式对待色彩。用这种方式，我们“移动”这些值。</p>
<p>这章我们学习如何运用矩阵变换来移动，旋转和缩放向量。除了之前章节学的图形，这些变换是创作的基础。在接下来的章节我们会应用我们所学的制作漂亮的程序纹理。你会发现编程的重复性和多样性是种令人兴奋的实践。</p>
</div>

                    <hr>
                    <ul class="navigationBar" >
                            <li class="navigationBar" onclick="previusPage()">&lt; &lt; Previous</li>
                            <li class="navigationBar" onclick="homePage()"> Home </li>
                            <li class="navigationBar" onclick="nextPage()">Next &gt; &gt;</li>
                        </ul>

                    <footer>
                        <p> Copyright 2015 <a href="http://www.patriciogonzalezvivo.com" target="_blank">Patricio Gonzalez Vivo</a> </p>
                    </footer>

                    <script src="/src/three.min.js"></script>

                    <script id="vertexShader" type="x-shader/x-vertex">
                        void main() {
                            gl_Position = vec4( position, 1.0 );
                        }
                    </script>

                    <script id="fragmentShader" type="x-shader/x-fragment">
                        uniform vec2 u_resolution;
                        uniform vec2 u_mouse;
                        uniform float u_time;

                        void main() {
                            vec2 st = gl_FragCoord.xy/u_resolution.xy;
                            gl_FragColor=vec4(st.x,st.y,0.0,1.0);
                        }
                    </script>

                    <script>
                        var camera, scene, renderer, clock;
                        var uniforms;
                        var mouse = { x: 0, y: 0 };

                        document.onmousemove = getMouseXY;

                        function getMouseXY (e) {
                            mouse.x = e.pageX;
                            mouse.y = e.pageY;
                        }

                        function init () {

                            camera = new THREE.Camera();
                            camera.position.z = 1;

                            scene = new THREE.Scene();
                            clock = new THREE.Clock();

                            var geometry = new THREE.PlaneBufferGeometry(2, 2);

                            uniforms = {
                                u_time: { type: "f", value: 1.0 },
                                u_mouse: { type: "v2", value: new THREE.Vector2() },
                                u_resolution: { type: "v2", value: new THREE.Vector2() }
                            };

                            var material = new THREE.ShaderMaterial({
                                uniforms: uniforms,
                                vertexShader: document.getElementById('vertexShader').textContent,
                                fragmentShader: document.getElementById('fragmentShader').textContent
                            });

                            var mesh = new THREE.Mesh(geometry, material);
                            scene.add(mesh);

                            renderer = new THREE.WebGLRenderer();
                            renderer.setPixelRatio(window.devicePixelRatio);

                            container.appendChild(renderer.domElement);

                            onWindowResize();
                            window.addEventListener('resize', onWindowResize, false);
                        }

                        function onWindowResize (event) {
                            renderer.setSize(window.innerWidth, window.innerHeight);
                            uniforms.u_resolution.value.x = renderer.domElement.width;
                            uniforms.u_resolution.value.y = renderer.domElement.height;
                            uniforms.u_mouse.value.x = mouse.x;
                            uniforms.u_mouse.value.y = mouse.y;
                        }

                        function animate () {
                            requestAnimationFrame(animate);
                            render();
                        }

                        function render () {
                            uniforms.u_time.value += clock.getDelta();
                            renderer.render(scene, camera);
                        }

                        var container = document.getElementById('container');

                        if (container) {

                            init();
                            animate();
                        }
                    </script>
                    <script type="text/javascript" src="/src/main.js" defer></script>
                </body>

            </html>